import os
import time
import logging
import hashlib
import cgi
import urllib

from time import gmtime, strftime
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
from google.appengine.ext import db

from oauthlib import oauth
from myspace.myspaceapi import MySpace
from models import Activity, Subscription, RawFeed
from usersession import User
from igoogle import IGoogleHandler
from admin import AdminPageHandler
from celebs import CelebsHandler

import ckeynsecret
import feedparser
import simplejson
import gmemsess
        
class MainPageHandler(webapp.RequestHandler):        
    def get(self):
        user_id = User.get_current_user_id(self)
        if user_id:
            user_name = User.get_current_user_name(self) 
            """ User is logged in """
            subscription = Subscription.get_user_subscription(str(user_id))
            #subscription = True
            if subscription:
                self.show_activity_page(user_name)
            else:
                self.show_flood_subscription_page(user_name)
        else:
            MainPageHandler.show_login_page(self)
            
    def show_flood_subscription_page(self, user_name):
        template_data = {
                'user_name' : user_name,
        }
        path = os.path.join(os.path.dirname(__file__), 'templates/subscribe.html')
        return self.response.out.write(template.render(path, template_data))

    def show_activity_page(self, user_name):
        template_data = {
                'user_name' : user_name,
        }
        path = os.path.join(os.path.dirname(__file__), 'templates/updates.html')
        return self.response.out.write(template.render(path, template_data))

    @staticmethod
    def show_login_page(req_handler):
        path = os.path.join(os.path.dirname(__file__), 'templates/login.html')
        return req_handler.response.out.write(template.render(path, {}))

class CrossDomainXmlHandler(webapp.RequestHandler):
    def get(self):
        path = os.path.join(os.path.dirname(__file__), 'templates/crossdomain.xml')
        self.response.headers['Content-Type'] = 'text/xml'
        self.response.out.write(template.render(path, {}))
        self.response.set_status(200)
        
""" Initiates MySpace OAuth login process
"""
class StartMySpaceAuth(webapp.RequestHandler):
    def get(self):
        session=gmemsess.Session(self)    
        callback_url = self.request.host_url + '/oauthcallback'
        ms = MySpace(ckeynsecret.CONSUMER_KEY, ckeynsecret.CONSUMER_SECRET)
        request_token = ms.get_request_token()
        auth_url = ms.get_authorization_url(request_token, callback_url)
        session['unauthed_token'] = request_token.to_string()
        session.save()
        self.redirect(auth_url)

""" Handles OAuth callback from MySpace
"""
class OAuthCallback(webapp.RequestHandler):
    def get(self):
        session=gmemsess.Session(self)
        unauthed_token = session['unauthed_token'] if 'unauthed_token' in session else None
        if not unauthed_token:
            self.response.out.write("No un-authed token found in session")
            return
        token = oauth.OAuthToken.from_string(unauthed_token)       
        if token.key != urllib.unquote( self.request.get('oauth_token', 'no-token') ):
            self.response.out.write("Something went wrong! Tokens do not match")
            return
        ms = MySpace(ckeynsecret.CONSUMER_KEY, ckeynsecret.CONSUMER_SECRET)
        access_token = ms.get_access_token(token)
        self.save_user_id_in_session(access_token.to_string())
        self.redirect('/')
    
    def save_user_id_in_session(self, str_access_token):
        access_token = oauth.OAuthToken.from_string(str_access_token)    
        ms = MySpace(ckeynsecret.CONSUMER_KEY, ckeynsecret.CONSUMER_SECRET, access_token.key, access_token.secret)
        user_id = ms.get_userid()
        user_name = ms.get_username()
        User.set_current_user(self, user_id, user_name)
        return user_id

""" Handles Client subscription options
"""
class ClientSubscriptionHandler(webapp.RequestHandler):
    def post(self):
        session=gmemsess.Session(self)
        user_id =  str( User.get_current_user_id(self) )
        
        # URL that will recieve the activity updates from MS
        callback_url = self.request.host_url + "/subscriber." + str(user_id)
        
        xml = self.request.get('query_xml')
        query_xml = xml.replace('__CALLBACK_URL__', callback_url)
        self.perform_subscription(user_id, query_xml)
        self.redirect('/')    
    
    def perform_subscription(self, user_id, query_xml):
        ms = MySpace(ckeynsecret.CONSUMER_KEY, ckeynsecret.CONSUMER_SECRET)
        status,subscription_id = ms.register_subscription(query_xml)
        if status == 201:
           subscription = Subscription(user=user_id, ms_subscription_id=subscription_id)
           subscription.put()

"""Called by Ajax client to get updates
"""
class GetUpdatesHandler(webapp.RequestHandler):        
    def get(self):
        current_user = str ( User.get_current_user_id(self) )
        query = Activity.gql("WHERE owner = :owner ORDER BY updated DESC", owner=current_user)

        activity = query.get()
        if activity:
            encoder = simplejson.JSONEncoder()
            json_activity = {
                        'time': str(activity.updated),
                        'title': activity.title,
                        'content': activity.content,
                        'source': activity.link,
                        'type': activity.object_type
                        }
            activity.delete()
            self.response.out.write( encoder.encode(json_activity) )
        else:
            # Nothing in the Queue
            self.response.out.write("Q_EMPTY")
            return        
    
"""Handles feed update callbacks from MySpace
   1. Finds out the target user for the update
   2. Parses the feed and stores in the datastore
""" 
class PubSubHandler(webapp.RequestHandler): 
    def post(self):
        logging.info('Recieved Content Update From Hub') 
        
        body = self.request.body.decode('utf-8')
        logging.info('Post body is %d characters', len(body))

        # find out which user this update is for
        target_user = self.get_target_user()
        if target_user is None:
            logging.info("Unknown target user. Ignoring update")
            return
        
        # Reject feeds if there's not a record of subscription on our end
        # i.e. we deleted a subscription, but, MySpace is still sending it our way
        user_subscription = Subscription.get_user_subscription(target_user)
        if user_subscription is None:
            logging.info('Unknown subscriber. Rejecting update')
            return
        
        # check to see if we need to save the entire feed for this user
        # mainly for debugging purposes to see if the feed POSTed is valid
        if (self.is_save_raw_feed_for_user(target_user)):
            self.save_feed(target_user, self.request.body)
            
        data = feedparser.parse(self.request.body)
        if data.bozo:
          logging.error('Bozo feed data. %s: %r',
                         data.bozo_exception.__class__.__name__,
                         data.bozo_exception)
          if (hasattr(data.bozo_exception, 'getLineNumber') and
              hasattr(data.bozo_exception, 'getMessage')):
            line = data.bozo_exception.getLineNumber()
            logging.error('Line %d: %s', line, data.bozo_exception.getMessage())
            #segment = self.request.body.split('\n')[line-1]
            #logging.info('Body segment with error: %r', segment.decode('utf-8'))
          return self.response.set_status(200) # Setting it to 200 so feed does not get deactivated
    
        logging.info('Found %d entries', len(data.entries))
        for entry in data.entries:
          if hasattr(entry, 'content'):
            # This is Atom.
            entry_id = entry.id
            content = entry.content[0].value
            link = entry.get('link', '')
            title = entry.get('title', '')
          else:
            content = entry.get('description', '')
            title = entry.get('title', '')
            link = entry.get('link', '')
            entry_id = (entry.get('id', '') or link or title or content)
          
          object_type = ''  
          if hasattr(entry, 'object-type'):
            object_type = entry.get('object-type', None) # Should be something like this: http://activitystrea.ms/schema/1.0/photo
            if object_type:
                if object_type[-1] == '/': # Strip trailing slash, if any
                    object_type = object_type[0:-1]
                object_type = object_type[object_type.rindex('/')+1:] # Extract the last word (song, status, photo etc)
          
          #logging.info('Found entry with title = "%s", id = "%s", '
          #             'link = "%s"',
          #             title, entry_id, link)
          
          activity = Activity(
              owner = target_user,
              title=title,
              object_type=object_type,
              content=content,
              link=link)
          try:
              activity.put()
          except:
              pass
          
        self.response.out.write("Update Processed.");
        self.response.set_status(200)

    """Figure out which user this hub request (verification/feed update) is for
       We tacked on the username to the callback url when we sent the subscription over to the
       hub. The request path should have that info in the form of "subscriber.<username>"
    """
    def get_target_user(self):
        req_path = self.request.path
        # look for a '.' in the path. If not, we don't know the user id
        # for whom this update is for
        if req_path.find('.') == -1:
            return None
        target_user = req_path.split('.')[1]
        return target_user
    
    """ Check to see if we need to save the raw feed for the user - for debugging/testing purposes
    """
    def is_save_raw_feed_for_user(self, user_id):
        # Get the user's subscription record
        subscription = Subscription.get_user_subscription(user_id)
        if (subscription):
           return subscription.save_raw_feeds 
        return False
    
    def save_feed(self, user_id, feed):
        rf = RawFeed()
        rf.owner = user_id
        rf.feed = db.Text(feed, encoding="utf_8")
        try:
            rf.put()
        except:
            pass
        
class RawFeedsPageHandler(webapp.RequestHandler):        
    def get(self):
        user_id = User.get_current_user_id(self)
        if user_id: 
            user_id = str(user_id)
            subscription = Subscription.get_user_subscription(user_id)
            if subscription:
                if (subscription.save_raw_feeds or RawFeed.get_raw_feed_count(user_id) > 0):
                    op = self.request.get('op', default_value=None)
                    if op is None:
                        raw_feeds = RawFeed.get_raw_feeds_from_datastore(user_id, 50)
                        template_data = { 'feeds' : raw_feeds}
                        self.show_raw_feeds_page(template_data)
                    else:
                        if op == 'get_feed':
                            self.send_feed_to_client()
                else:
                    template_data = { 'error' : 'This user id does not have the setting that saves raw feeds. Please talk to Chak if you need this functionality'}
                    self.show_raw_feeds_page(template_data)
        else:
            MainPageHandler.show_login_page(self)

    def post(self):
        op = self.request.get('op', default_value=None)
        if op is None:
            self.show_raw_feeds_page()
        else:
            if op == 'delete_feeds':
                self.delete_feeds()
            
    def show_raw_feeds_page(self, template_data):
        path = os.path.join(os.path.dirname(__file__), 'templates/rawfeeds.html')
        return self.response.out.write(template.render(path, template_data))
        
    def send_feed_to_client(self):
        key_name = self.request.get('key', default_value=None)
        if key_name is not None:
            feed_obj = db.get(db.Key(key_name))
            self.response.headers['Content-Type'] = 'application/atom+xml'
            self.response.out.write(feed_obj.feed)
            self.response.set_status(200)

    def delete_feeds(self):
        key_names = self.request.get_all('key')
        for key_name in key_names:
            db.delete(db.Key(key_name))
        user_id = User.get_current_user_id(self)
        raw_feeds = RawFeed.get_raw_feeds_from_datastore(str(user_id), 50)
        template_data = { 'feeds' : raw_feeds}        
        self.show_raw_feeds_page(template_data)
    
application = webapp.WSGIApplication(
                    [
                         ('/', MainPageHandler),
 
                         ('/crossdomain.xml', CrossDomainXmlHandler),
                         
                         # Oauth Related
                         ('/startauth', StartMySpaceAuth),
                         ('/oauthcallback', OAuthCallback),

                         # Client Subscription related
                         ('/subscribe_to_ms', ClientSubscriptionHandler), 
                         
                         # Hub callback related
                         ('/subscriber.*', PubSubHandler), # Using ".*" so we can handle multiple subscriptions
                         
                         # Client AJAX related
                         ('/getupdates', GetUpdatesHandler), 
                         
                         # Admin screen 
                         ('/admin', AdminPageHandler),

                         # Raw Feeds screen 
                         ('/rawfeeds', RawFeedsPageHandler),

                         # Celebs handler
                         ('/entertainment', CelebsHandler),                         
                         
                         # iGoogle gadget  handler
                         ('/ig', IGoogleHandler),                         
                    ], 
                    debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()
